﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using CthulhuDomain;
using CthulhuDomain.GameEntities;
using CthulhuDomain.Services;
using CthulhuDomain.StageManagement;
using CthulhuDomain.Strategies;
using Microsoft.Pex.Framework;
using Microsoft.Pex.Framework.Validation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Invaders.Test.Factories.GameEntities
{
  [TestClass]
  public partial class ChtulhuFixture
  {
    private static ContentManager contentManager;
    private SpriteBatchAdapter spriteBatch;
    private GameServiceManager gameServiceManager;

    [TestInitialize]
    public void TestInitialize() {
      var gd = new MockedGraphicsDeviceService(DeviceType.Hardware, GraphicsProfile.HiDef);
      gd.CreateDevice();

      string activeConfinguration;
#if DEBUG
      activeConfinguration = "Debug";
#else
      activeConfinguration = "Release";

#endif
      spriteBatch = new SpriteBatchAdapter(gd.GraphicsDevice);
      contentManager = new ContentManager(gd.ServiceProvider) {
        RootDirectory = Path.GetFullPath(string.Format("../../../CthulhuInvaders/bin/x86/{0}/Content/", activeConfinguration))
      };

      gameServiceManager = new GameServiceManager(new GameAdapter(new Game()), new ContentManagerAdapter(contentManager), spriteBatch, new RectangleAdapter(0, 0, 300, 300));
    }

    [TestCleanup]
    public void MyTestCleanup() {
      contentManager = null;
      spriteBatch = null;
      gameServiceManager = null;
    }

    [PexMethod]
    public void CthulhuXPosition_AfterUpdate_ShouldBeIncreasedByOwnWidth() {
      // Arrange
      var chtulhu = DomainFactory
        .GetFactory(DomainFactory.eTypeEntity.Cthulhu)
        .CreateEntity(new ContentManagerAdapter(contentManager), new CthulhuState() { LifePoints = 1, StartingPosition = new Vector2Adapter(0, 0) });
      var oldXPos = chtulhu.Position.X;
      var context = new RectangleAdapter(0, 0, 300, 300);
      var gameTime = new GameTimeAdapter(new GameTime(new TimeSpan(0, 0, 1, 0), new TimeSpan(0, 0, 1, 10)));
      // Act
      chtulhu.Move(context, gameTime);
      chtulhu.Update(context, gameTime);
      // Assert
      Trace.WriteLine(string.Format("Old X Coord was: {0}; New X Coord is: {1}", oldXPos, chtulhu.Position.X));
      Assert.IsTrue(oldXPos < chtulhu.Position.X);
    }

    [PexMethod]
    public DomainEntity Move_PositionMaxWidth_SpeedLowerThenZero() {
      // Arrange
      var context = new RectangleAdapter(0, 0, 300, 300);
      var entity = DomainFactory
                                .GetFactory(DomainFactory.eTypeEntity.Cthulhu)
                                .CreateEntity(new ContentManagerAdapter(contentManager),
                                              new CthulhuState() { LifePoints = 1, StartingPosition = new Vector2Adapter(context.Width,0) });
      var oldSpeed = entity.Speed;
      var gameTime = new GameTimeAdapter(new GameTime(new TimeSpan(0, 0, 1, 0), new TimeSpan(0, 0, 1, 10)));
      // Act
      entity.Move(context, gameTime);
      entity.Update(context, gameTime);

      // Assert
      Trace.WriteLine(string.Format("Old Speed : {0}; New Speed: {1}", oldSpeed, entity.Speed));
      Assert.IsTrue(entity.Speed < 0);

      return entity;
    }

    [PexMethod]
    public void Update_ShootGreaterThenZero_ShootYIncrease()
    {
      // Arrange
      var entity = Move_PositionMaxWidth_SpeedLowerThenZero();
      entity.AddShoot();
      var context = new RectangleAdapter(0, 0, 300, 300);
      var gameTime = new GameTimeAdapter(new GameTime(new TimeSpan(0, 0, 1, 0), new TimeSpan(0, 0, 1, 10)));
      var oldXPos = entity.Shoots[0].Position.Y;
      // Act
      entity.Move(context, gameTime);
      entity.Update(context, gameTime);
      // Assert
      Trace.WriteLine(string.Format("Old Position Y : {0}; New Position Y : {1}", oldXPos, entity.Shoots[0].Position.Y));
      Assert.IsTrue(entity.Shoots[0].Position.Y > oldXPos);
    }

    [PexMethod]
    public void Update_PowerUpGreaterThenZero_PowerUpYIncrease()
    {
      // Arrange
      CollisionManager.Initialize(gameServiceManager, (LevelStage)StageFactory.GetFactory(StageFactory.eTypeStage.Level, gameServiceManager).CreateEntity());
      var entity = Move_PositionMaxWidth_SpeedLowerThenZero();
      entity.AddPowerUp();
      var context = new RectangleAdapter(0, 0, 300, 300);
      var gameTime = new GameTimeAdapter(new GameTime(new TimeSpan(0, 0, 1, 0), new TimeSpan(0, 0, 1, 10)));
      var oldXPos = CollisionManager.GetPowerUp(0).Position.Y;
      // Act
      CollisionManager.CurrentStage.Update(context, new Keys[] {Keys.None}, gameTime);
      
      //entity.Move(context, gameTime);
      //entity.Update(context, gameTime);
      // Assert
      Trace.WriteLine(string.Format("Old Position Y : {0}; New Position Y : {1}", oldXPos, CollisionManager.GetPowerUp(0).Position.Y));
      Assert.IsTrue(entity.PowerUps[0].Position.Y > oldXPos);
    }

    [PexMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void RandomNext_MinGreaterThenMax_ThrowInvalidArgumentException(int min, int max)
    {
      // Assume
      PexAssume.IsTrue(max > 0 && min > max);
      // Arrange
      // Act
      try
      {
        StaticRandomNumberGenerator.RandomNext(min, max);
      }
      catch (Exception e)
      {
        Trace.WriteLine(e);
      }
      // Assert
    }

    [PexMethod]
    public void RandomNext_MaxGreaterThenMinAndGreaterThenZero_ReturnIntegerInBound(int min, int max)
    {
      // Assume
      PexAssume.IsTrue(max > min);
      // Arrange
      // Act
      int result = StaticRandomNumberGenerator.RandomNext(min, max);
      // Assert
      Assert.IsTrue(result <= max && result >= min);
    }

    [PexMethod, PexAllowedException(typeof(InvalidEnumArgumentException))]
    public PowerUpFactory GetFactory_TypeFactoryInbound_ReturnValidFactory(PowerUpFactory.ePowerUp type)
    {
      // Assume
      // Arrange
      // Act
      var factory = PowerUpFactory.GetFactory(type);
      // Assert
      Assert.IsNotNull(factory);
      return factory;
    }
  } 
}
